// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.parsers;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import net.web2me.api.AbstractBaseParser;
import net.web2me.api.ParserException;
import net.web2me.api.util.DocumentGenerator;
import net.web2me.api.util.Downloader;
import net.web2me.api.util.UtilityException;
import net.web2me.api.util.Validator;

public class StandardParser extends AbstractBaseParser {
	
	private static Logger logger = Logger.getLogger(StandardParser.class);
	
	private final static String PROP_REGEX = "regex";
	
	private final static String PROP_TYPES = "types";
	
	private final static String PROP_LARGER_THAN = "larger.than";
	
	private final static String LARGER_THAN_FORMAT_REGEX = "(\\d)+[bkmgt]$";
	
	private String regex;

	private boolean errorShowed = false;

	public void process(String siteStream) throws ParserException {
		
		if(System.getProperty("types") == null){
			logger.warn("No file types specified. Use the system property -Dtypes=zip:mp3:exe");
			logger.warn("Assuming images and videos");
		}
		
		regex = System.getProperty(PROP_REGEX);

		Document site = null;
		try {
			site = DocumentGenerator.generate(siteStream);
		} catch (Exception e) {
			throw new ParserException(e);
		}

		NodeList objectTags = site.getElementsByTagName("object");
		
		NodeList links = site.getElementsByTagName("a");

		processNodeList(objectTags);
		
		processNodeList(links);
	}

	private void processNodeList(NodeList list) throws ParserException {
		
		Downloader downloader = new Downloader();
		
		for (int i = 0; i < list.getLength(); i++) {
			Element object = (Element) list.item(i);
			String url = object.getAttribute("href");
			
			if((regex != null && url.matches(regex)) || regex == null){
				if (Validator.isValidFileType(url, getFileTypes())) {
					try {
						url = toAbsoluteUrl(url);

						downloader.setUrl(url);
						
						if(System.getProperty(PROP_LARGER_THAN) != null){
							// Only download it if it's largen that the size specified
							if(downloader.getFileSize() < getLargenThanSize()){
								// The file to be downloaded is smaller than the size
								continue;
							}
						}
						
						downloader.setDestinyDirectory(getDir());
						
						logger.debug("Downloading file: " + url);
						
						new Thread(downloader, url.toString()).start();
					} catch (RuntimeException e) {
						throw new ParserException(e);
					}
				}
			}
		}
	}

	public String toAbsoluteUrl(String url) {
		logger.debug("Transforming \"" + url + "\" to an absolute URL");
		
		String finalUrl = "";

		if (url.indexOf("http://", 0) == -1) {
			try {
				if (url.substring(0, 1).equals("/")) {
					String host = getUrl().substring(7,
							getUrl().indexOf("/", 7));
					finalUrl = "http://" + host + url;
				} else {
					logger.debug("Before anything: " + finalUrl);
					finalUrl = getUrl();
					
					// Remove the querystring
					if (finalUrl.indexOf("?") > -1) {
						finalUrl = getUrl().substring(0, getUrl().indexOf("?"));
					}
					
					logger.debug("Before 2nd step: " + finalUrl);

					// Remove any web page after the last slash
					if (finalUrl.lastIndexOf("/") > -1) {
						finalUrl = finalUrl.substring(0, finalUrl.lastIndexOf("/") + 1);
					}
					
					logger.debug("Before last step: " + finalUrl);

					if (finalUrl.equals("")) {
						finalUrl = getUrl() + "/" + url;
					} else {
						finalUrl += url;
					}
				}
			} catch (Exception e) {
				logger.error("Exception caught", e);
			}
		} else {
			finalUrl = url;
		}

		logger.debug("Absolute URL: " + finalUrl);
		
		return finalUrl;
	}

	public String[] getFileTypes() {
		String tmpTypes = System.getProperty(PROP_TYPES);
		
		if(tmpTypes != null){
			return tmpTypes.split(":");
		}
		
		return new String[] { "jpg", "jpeg", "png", "mpg", "wmv", "mpeg", "mov", "qt", "avi" };
	}
	
	private int getLargenThanSize(){
		String givenSize = System.getProperty(PROP_LARGER_THAN);
		int finalSize = 0;
		
		
		if(givenSize.matches(LARGER_THAN_FORMAT_REGEX)){
			if(givenSize.endsWith("b")){
				// Bytes
				return Integer.parseInt(givenSize.replace("b", ""));
			}else if(givenSize.endsWith("k")){
				// Kilobytes
				return Integer.parseInt(givenSize.replace("k", "")) * 1024;
			}else if(givenSize.endsWith("m")){
				// Megabytes
				return Integer.parseInt(givenSize.replace("m", "")) * 1024 * 1024;
			}else if(givenSize.endsWith("g")){
				// Gigabytes
				return Integer.parseInt(givenSize.replace("g", "")) * 1024 * 1024 * 1024;
			}else if(givenSize.endsWith("t")){
				// Terabytes
				return Integer.parseInt(givenSize.replace("t", "")) * 1024 * 1024 * 1024 * 1024;
			}else{
				showLargenThanSizeError();
			}
		}else{
			showLargenThanSizeError();
		}
		
		return finalSize;
	}
	
	private void showLargenThanSizeError(){
		if(!errorShowed){
			errorShowed = true;
			
			logger.warn("The specified \"" + PROP_LARGER_THAN + "\" property is not valid");
			logger.warn("Specify it as a number followed by: b, k, m, g or t (Bytes, Kilobytes, Megabytes, Gigabytes and Terabytes");
			logger.warn("E.g: 100k");
		}
	}
}
